package com.hsc.www.common;

import com.alibaba.fastjson.serializer.SerializerFeature;
import com.alibaba.fastjson.support.config.FastJsonConfig;
import com.alibaba.fastjson.support.spring.FastJsonHttpMessageConverter;
import org.springframework.http.*;
import org.springframework.http.converter.HttpMessageConverter;
import org.springframework.util.LinkedMultiValueMap;
import org.springframework.util.MultiValueMap;
import org.springframework.web.client.RestTemplate;

import java.util.*;

public class OAuthUtil {


    private RestTemplate restTemplate;

    private String authorizationServerUrl;
    private String clientId;
    private String clientSecret;

    private String tokenUir = "/oauth/token";
    private String checkTokenUir = "/oauth/check_token";
    private String tokenKeyUir = "/oauth/token_key";


    public OAuthUtil(String authorizationServerUrl, String clientId, String clientSecret) {
        this.authorizationServerUrl = authorizationServerUrl;
        this.clientId = clientId;
        this.clientSecret = clientSecret;
        this.restTemplate = new RestTemplate();
        AddConfigureMessageConverters(this.restTemplate, getFastJsonHttpMessageConverter());

    }

    /**
     * 获取公钥
     * 在使用jwt时一般会采用RSA非对称加密，受权服务器会发布自己的公钥，公钥可以随意获取。
     * 受权服务器用私钥对token进行签名，资源服务器用公钥验证token签名。
     *
     * @return
     */
    public TokenKey getTokenKey() {
        ResponseEntity<TokenKey> responseEntity = this.restTemplate.getForEntity(authorizationServerUrl + tokenKeyUir, TokenKey.class);
        return responseEntity.getBody();
    }

    /**
     * 密码模式
     *
     * @param username    资源所有者 用户名
     * @param password    资源所有者 密码
     * @param redirectUrl
     * @return
     * @throws Exception
     */
    public Token getToken(String username, String password, String redirectUrl) throws Exception {
        String grantType = "password";
        HttpHeaders headers = new HttpHeaders();
        addBasicAuthorization(headers, this.clientId, this.clientSecret);
        MultiValueMap<String, String> body = new LinkedMultiValueMap<>();

        body.add("username", username);
        body.add("password", password);
        body.add("grant_type", grantType);
        body.add("redirect_url", redirectUrl);
        HttpEntity httpEntity = new HttpEntity(body, headers);
        ResponseEntity<Token> responseEntity = this.restTemplate.exchange(authorizationServerUrl + tokenUir, HttpMethod.POST, httpEntity, Token.class);
        return responseEntity.getBody();

    }

    /**
     * 受权码模式
     *
     * @param code        受权码
     * @param redirectUrl
     * @return
     * @throws Exception
     */
    public Token getToken(String code, String redirectUrl) throws Exception {
        String grantType = "authorization_code";
        HttpHeaders headers = new HttpHeaders();
        addBasicAuthorization(headers, this.clientId, this.clientSecret);
        Map body = new HashMap<>();
        body.put("code", code);
        body.put("grant_type", grantType);
        body.put("redirect_url", redirectUrl);
        HttpEntity httpEntity = new HttpEntity(body, headers);
        ResponseEntity<Token> responseEntity = new RestTemplate().exchange(authorizationServerUrl + tokenUir, HttpMethod.POST, httpEntity, Token.class);
        return responseEntity.getBody();
    }


    /**
     * 验证token，验证成功会返回 资源所有者信息 和 客户端信息
     *
     * @param tokenValue
     * @return
     * @throws Exception
     */
    public CheckToken getCheckToken(String tokenValue) throws Exception {
        HttpHeaders headers = new HttpHeaders();
        addBasicAuthorization(headers, this.clientId, this.clientSecret);
        HttpEntity httpEntity = new HttpEntity(null, headers);
        ResponseEntity<CheckToken> responseEntity = this.restTemplate.exchange(authorizationServerUrl + checkTokenUir + "?token={0}", HttpMethod.GET, httpEntity, CheckToken.class, tokenValue);
        return responseEntity.getBody();
    }


    public <T> T loadResourceByGet(String tokenValue, String type, String resourceUrl, Class<T> responseType, Object... uriVariables) throws Exception {

        HttpHeaders headers = new HttpHeaders();
        addAuthorization(headers, type, tokenValue);
        HttpEntity httpEntity = new HttpEntity(null, headers);
        ResponseEntity<T> responseEntity = this.restTemplate.exchange(resourceUrl, HttpMethod.GET, httpEntity, responseType, uriVariables);
        return responseEntity.getBody();


    }

    public <T> T loadResourceByPost(String tokenValue, String type, String resourceUrl, Class<T> responseType, MultiValueMap body) throws Exception {

        HttpHeaders headers = new HttpHeaders();
        addAuthorization(headers, type, tokenValue);
        HttpEntity httpEntity = new HttpEntity(body, headers);
        ResponseEntity<T> responseEntity = this.restTemplate.exchange(resourceUrl, HttpMethod.POST, httpEntity, responseType);
        return responseEntity.getBody();

    }


    private void addBasicAuthorization(HttpHeaders headers, String clientId, String clientSecret) throws Exception {
        String value = Base64.getEncoder().encodeToString((clientId + ":" + clientSecret).getBytes("UTF-8"));
        addAuthorization(headers, "Basic", value);
    }

    private void addAuthorization(HttpHeaders headers, String type, String value) throws Exception {
        headers.add("Authorization", type + " " + value);
    }


    public void AddConfigureMessageConverters(RestTemplate restTemplate, HttpMessageConverter... converters) {

        List<HttpMessageConverter<?>> messageConverters = restTemplate.getMessageConverters();
        for (HttpMessageConverter httpMessageConverter : converters) {
            messageConverters.add(httpMessageConverter);
        }
        restTemplate.setMessageConverters(messageConverters);

    }

    public HttpMessageConverter getFastJsonHttpMessageConverter() {
        FastJsonHttpMessageConverter fastConverter = new FastJsonHttpMessageConverter();
        FastJsonConfig fastJsonConfig = new FastJsonConfig();
        fastJsonConfig.setSerializerFeatures(SerializerFeature.WriteMapNullValue,
                SerializerFeature.QuoteFieldNames, SerializerFeature.DisableCircularReferenceDetect);
        fastConverter.setFastJsonConfig(fastJsonConfig);

        List<MediaType> fastMediaTypes = new ArrayList<>();
        fastMediaTypes.add(MediaType.APPLICATION_JSON_UTF8);
        fastConverter.setSupportedMediaTypes(fastMediaTypes);
        return fastConverter;
    }


}
